Security
The WTD Token Protocol employs multiple security patterns across all contracts.
Access Control
All contracts use OpenZeppelin's AccessControl for role-based permissions. Each function that modifies state is restricted to specific roles.
WTDToken
DEFAULT_ADMIN_ROLE, GOVERNANCE_ROLE
WTDSale
DEFAULT_ADMIN_ROLE, MULTI_SIG_ROLE
WTDVesting
DEFAULT_ADMIN_ROLE, VESTING_ROLE
WTDLiquidityManager
DEFAULT_ADMIN_ROLE
See Roles & Permissions for a full breakdown.
Reentrancy Protection
All contracts that handle token transfers inherit ReentrancyGuard from OpenZeppelin. The nonReentrant modifier is applied to:
All purchase functions in WTDSale
All claim functions in WTDVesting
All withdrawal functions in WTDVesting
Lock, unlock, and fee collection in WTDLiquidityManager
Safe Token Transfers
All ERC20 token transfers use OpenZeppelin's SafeERC20 library, which:
Checks return values from
transferandtransferFromHandles tokens that don't return a boolean (like some USDT implementations)
Reverts on failed transfers
Chainlink Oracle Validation
The WTDSale contract validates the Chainlink ETH/USD price feed with:
Price check: Reverts if the oracle returns a non-positive price (
InvalidOraclePrice)Staleness check: Reverts if the price data is older than 1 hour (
StaleOraclePrice)
Pausable Token
The WTDToken can be paused by the admin, which halts all token transfers including:
Regular transfers
Minting (allocation claims)
Burning
This provides an emergency brake if a vulnerability is discovered.
VESTING_ROLE Lock
The WTDVesting contract allows permanently locking the VESTING_ROLE. Once locked:
No new addresses can be granted VESTING_ROLE
Existing VESTING_ROLE cannot be revoked
This ensures only the originally authorized sale contract can create vesting schedules
The lock is achieved by setting the role's admin to a role that nobody holds
One-Time Configuration
Several operations are intentionally limited to a single execution:
Round configuration (WTDSale) — each round can only be configured once
TGE timestamp (WTDVesting) — can only be set once
VESTING_ROLE lock (WTDVesting) — can only be locked once
Allocation claims (WTDToken) — each category is claimed in full once
Immutable Contract References
WTDSale stores references to WTDToken, WTDVesting, USDC, USDT, and the Chainlink oracle as immutable variables. These cannot be changed after deployment, preventing address substitution attacks.
UUPS Upgrade Safety
The WTDToken implementation constructor calls _disableInitializers() to prevent the implementation contract from being initialized directly (only the proxy can be initialized).